#include <cassert>
#include <iostream>
#include <cstdlib>
#include "./../Float/float.h"

using namespace std;
typedef Float inFloat;

class Complex{

 private: 			
	inFloat real;
	inFloat imaginario;

 public:
 
 	/**
 	 * Constructores
 	 */
	Complex();
	Complex(const inFloat&, const inFloat&);
	Complex(const float, const float);
	Complex(const Complex&);
	
	/**
	 *operadores
	 */
	Complex& suma(const Complex&); // a este complejo this le sumo el del parámetro y también resta 
	Complex& producto(const Complex&);
	Complex& producto(const inFloat&); // producto x un lambda - REQUIERE el friend operator* en inFloat.h 
	inFloat moduloModulo() const; //Sería el módulo | |  al cuadrado (para evitar hacer raíz sobre el infloat)
	Complex& cuadrado();  
	friend Complex operator+(const Complex&, const Complex&); // REQUEIRE: friend operator+ definido en inFloat
	friend Complex operator-(const Complex&, const Complex&); // REQUEIRE: friend operator- definido en inFloat
	friend Complex operator*(const Complex&, const Complex&); // REQUIERE: friend operator* definido en inFloat
	friend Complex operator*(const inFloat&, const Complex&); // Escalar! requiere lo de arriba	
	
	/**
	 * getters
	 */
	inFloat Re() const; // proyector de la componente real
	inFloat Im() const; // id. de la componenete imaginaria

	/**
	 * setters
	 */
	Complex& setRe(inFloat);
	Complex& setIm(inFloat);	

	/**
	 * comparaciones y asignacion
	 */
	bool operator==(const Complex&) const; 
	bool operator!=(const Complex&) const;	
	Complex& operator=(const Complex&);
};

 	/*******************************
 	 * Constructores
 	 *******************************/
Complex::Complex(){
	inFloat real();
	inFloat imaginario();
}

Complex::Complex(const float re, const float im){
		Float a(re);
		Float b(im);
		this->real = re;
		this->imaginario =  im;
}

Complex::Complex(const inFloat& re, const inFloat& im){
	this->real = re;
	this->imaginario = im;
}

Complex::Complex(const Complex& z){
	this->real = z.Re();
	this->imaginario = z.Im();
}

	/*****************************
	 *operadores
	 ****************************/
Complex operator+(const Complex& z, const Complex& w){
	Complex ret(z);
	ret.suma(w);
	return ret;
}

Complex operator-(const Complex& z, const Complex& w){
	return Complex( z.Re()-w.Re(), z.Im()-w.Im() );
}

Complex operator*(const Complex& z, const Complex& w){
	Complex ret(z);
	ret.producto(w);
	return ret;
}

Complex operator*(const inFloat& lambda, const Complex& z){
	Complex ret(z);
	ret.producto(lambda);
	return ret;
}


Complex& Complex::suma(const Complex& z){
	this->real += z.Re();
	this->imaginario += z.Im();
	return *this;
}

Complex& Complex::producto(const Complex& z){

	// por comodidad a this->imaginario lo llamo wi y a this->real lo llamo wr
	inFloat wr = this->real;
	this->real *= z.Re();
	this->real = this->real - (this->imaginario * z.Im());
	this->imaginario *= z.Re();
	wr*= z.Im();
	this->imaginario += wr;
	
	
	return *this;
}

Complex& Complex::producto(const inFloat& lambda){
	this->real *= lambda;
	this->imaginario *= lambda; 
	return *this;
}

Complex& Complex::cuadrado(){
	inFloat wr = this->real; 
	
	this->real += this->imaginario;
	this->real *= (wr - this->imaginario);
	Float dos("2.0");
	this->imaginario *= dos;
	this->imaginario *= wr;
	
	
	return *this;
}
inFloat Complex::moduloModulo() const{
	inFloat ret(this->real);
	inFloat im(this->imaginario);
	ret*=ret;
	im *= im;
	ret+=im;
	return ret;
}

inFloat Complex::Re() const {
	return this->real;
}


inFloat Complex::Im() const {
	return this->imaginario;
}

/*********************
** comparaciones y asignaciones
*****************************/
bool Complex::operator==(const Complex& z) const{
	return (this->real == z.Re()) && (this->imaginario == z.Im());

}

bool Complex::operator!=(const Complex& z) const{
	return !(*this == z);
}


Complex& Complex::operator=(const Complex& z){
	if (this != &z){
		this->real = z.Re();
		this->imaginario = z.Im();
	}
	return *this;

}
/*********************
** setter
**************************/
Complex& Complex::setRe(inFloat r){
	this->real = r;
	return *this;
}

Complex& Complex::setIm(inFloat i){ // referenciaaaaaaaaaa :'(
	this->imaginario = i;
	return *this;
}

/*******************
** STREAM
**********************/
ostream& operator<<(ostream& out, const Complex& z) {
	out<<"("<< z.Re()<<"," << z.Im()<< ")";
	return out;
//coutea en forma de (Re,Im) :)
}

